#ifndef __DEFENS_SYNC_FUNC_H
#define __DEFENS_SYNC_FUNC_H

#include "../../../../include/util.h"

#include "llvm/DerivedTypes.h"
#include "llvm/Instructions.h"
#include "llvm/Constants.h"
#include "llvm/BasicBlock.h"
#include "llvm/Pass.h"
#include "llvm/Support/CFG.h"
#include "llvm/Transforms/Utils/BasicInliner.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include "llvm/ADT/DenseSet.h"

#include <set>

using namespace llvm;
using namespace std;

namespace defens {

	struct SyncFunc: public ModulePass {
		
		static char ID;
		DenseMap<Instruction *, vector<Function *> > called_func; // Computed in construct_call_graph
		DenseMap<Function *, vector<Instruction *> > call_sites; // Computed in construct_call_graph
		DenseMap<Function *, vector<Instruction *> > exits; // Computed in construct_call_graph
		DenseMap<Function *, Function *> parent; // Used in DFS
		DenseSet<Function *> visited;
		vector<Function *> sync_funcs;

		SyncFunc(): ModulePass(&ID) {}

		virtual bool runOnModule(Module &M);
		virtual void print(llvm::raw_ostream &O, const Module *M) const;
		bool contains_sync_operation(Function *f);
		vector<Function *> get_called_functions(Instruction *ins);
		bool is_sync_function(Function *f);

	private:
		bool is_exit_block(BasicBlock *bb);
		void construct_call_graph(Module &M);
		void setup_sync_functions(Module &M);
		//bool no_lock(const Function *f);
		void DFS(Function *f);
		void output(const Module &M) const;
		void traverse_call_graph(Module &M);
		void print_call_chain(Function *f);
		void print_calling_functions(Function *f);
		void stats(const Module &M) const;
	};
}

#endif
